﻿//-----------------------------------------------------------------------
// <copyright file="GoogleConsumer.cs" company="Andrew Arnott">
//     Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Configuration;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;

namespace Web.HelperClasses {
	/// <summary>
	/// A consumer capable of communicating with Google Data APIs.
	/// </summary>
	public static class GoogleConsumer
	{
		private static ServiceProviderDescription serviceDescription;
		private static X509Certificate2 signingCertificate;

		/// <summary>
		/// The Consumer to use for accessing Google data APIs.
		/// </summary>
		public static ServiceProviderDescription ServiceDescription
		{
			get
			{
				if (serviceDescription == null)
					serviceDescription = new ServiceProviderDescription
					                     	{
					                     		RequestTokenEndpoint =
					                     			new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken",
					                     			                             HttpDeliveryMethods.AuthorizationHeaderRequest |
					                     			                             HttpDeliveryMethods.GetRequest),
					                     		UserAuthorizationEndpoint =
					                     			new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken",
					                     			                             HttpDeliveryMethods.AuthorizationHeaderRequest |
					                     			                             HttpDeliveryMethods.GetRequest),
					                     		AccessTokenEndpoint =
					                     			new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken",
					                     			                             HttpDeliveryMethods.AuthorizationHeaderRequest |
					                     			                             HttpDeliveryMethods.GetRequest),
												TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
												//TamperProtectionElements =
												//    new ITamperProtectionChannelBindingElement[]
												//        {new RsaSha1SigningBindingElement(SigningCertificate)}
					                     	};
				return serviceDescription;
			}
		}

		private static X509Certificate2 SigningCertificate
		{
			get
			{
				if(signingCertificate == null)
					signingCertificate = new X509Certificate2(Convert.FromBase64String(ConfigurationManager.AppSettings["googleX509Cert"]));
				return signingCertificate;
			}
		}

		/// <summary>
		/// A mapping between Google's applications and their URI scope values.
		/// </summary>
		private static readonly Dictionary<Applications, string> DataScopeUris = new Dictionary<Applications, string> {
			{ Applications.Analytics, "https://www.google.com/analytics/feeds/" },
			{ Applications.GoogleBase, "http://www.google.com/base/feeds/" },
			{ Applications.Blogger, "http://www.blogger.com/feeds" },
			{ Applications.BookSearch, "http://www.google.com/books/feeds/" },
			{ Applications.Calendar, "http://www.google.com/calendar/feeds/" },
			{ Applications.Contacts, "http://www.google.com/m8/feeds/" },
			{ Applications.DocumentsList, "http://docs.google.com/feeds/" },
			{ Applications.Finance, "http://finance.google.com/finance/feeds/" },
			{ Applications.Gmail, "https://mail.google.com/mail/feed/atom" },
			{ Applications.Health, "https://www.google.com/h9/feeds/" },
			{ Applications.Maps, "http://maps.google.com/maps/feeds/" },
			{ Applications.OpenSocial, "http://sandbox.gmodules.com/api/" },
			{ Applications.PicasaWeb, "http://picasaweb.google.com/data/" },
			{ Applications.Spreadsheets, "http://spreadsheets.google.com/feeds/" },
			{ Applications.WebmasterTools, "http://www.google.com/webmasters/tools/feeds/" },
			{ Applications.YouTube, "http://gdata.youtube.com" },
		};

		/// <summary>
		/// The URI to get contacts once authorization is granted.
		/// </summary>
		private static readonly MessageReceivingEndpoint GetContactsEndpoint = new MessageReceivingEndpoint("http://www.google.com/m8/feeds/contacts/default/full/", HttpDeliveryMethods.GetRequest);
        
		private static readonly MessageReceivingEndpoint GetGmailEndpoint = new MessageReceivingEndpoint("https://mail.google.com/mail/b/alexandr.v.golubev%40gmail.com/full/", HttpDeliveryMethods.GetRequest);
        //private static MessageReceivingEndpoint GetProfileImageEndpoint(string email)
        //{
        //    var emailparts = email.Split(new string[]{"@"}, StringSplitOptions.None);
        //    if(emailparts.Length != 2) throw new ArgumentException("Invalid email " + email);
        //    string user = emailparts[0];
        //    string server = emailparts[1];
        //    string url = String.Format("https://www.google.com/m8/feeds/photos/profile/{0}/{1}", server, user);
        //    return new MessageReceivingEndpoint(url, HttpDeliveryMethods.GetRequest);
        //}


		/// <summary>
		/// The many specific authorization scopes Google offers.
		/// </summary>
		[Flags]
		public enum Applications : long {
			/// <summary>
			/// The Gmail address book.
			/// </summary>
			Contacts = 0x1,

			/// <summary>
			/// Appointments in Google Calendar.
			/// </summary>
			Calendar = 0x2,

			/// <summary>
			/// Blog post authoring.
			/// </summary>
			Blogger = 0x4,

			/// <summary>
			/// Google Finance
			/// </summary>
			Finance = 0x8,

			/// <summary>
			/// Google Gmail
			/// </summary>
			Gmail = 0x10,

			/// <summary>
			/// Google Health
			/// </summary>
			Health = 0x20,

			/// <summary>
			/// Google OpenSocial
			/// </summary>
			OpenSocial = 0x40,

			/// <summary>
			/// Picasa Web
			/// </summary>
			PicasaWeb = 0x80,

			/// <summary>
			/// Google Spreadsheets
			/// </summary>
			Spreadsheets = 0x100,

			/// <summary>
			/// Webmaster Tools
			/// </summary>
			WebmasterTools = 0x200,

			/// <summary>
			/// YouTube service
			/// </summary>
			YouTube = 0x400,

			/// <summary>
			/// Google Docs
			/// </summary>
			DocumentsList = 0x800,

			/// <summary>
			/// Google Book Search
			/// </summary>
			BookSearch = 0x1000,

			/// <summary>
			/// Google Base
			/// </summary>
			GoogleBase = 0x2000,

			/// <summary>
			/// Google Analytics
			/// </summary>
			Analytics = 0x4000,

			/// <summary>
			/// Google Maps
			/// </summary>
			Maps = 0x8000,
		}

	/// <summary>
		/// Requests authorization from Google to access data from a set of Google applications.
		/// </summary>
		/// <param name="consumer">The Google consumer previously constructed using <see cref="CreateWebConsumer"/> or <see cref="CreateDesktopConsumer"/>.</param>
		/// <param name="requestedAccessScope">The requested access scope.</param>
		public static void RequestAuthorization(WebConsumer consumer, Applications requestedAccessScope, Uri callback) {
			if (consumer == null) {
				throw new ArgumentNullException("consumer");
			}

			var extraParameters = new Dictionary<string, string> {
				{ "scope", GetScopeUri(requestedAccessScope) },
			};
			//Uri callback = Util.GetCallbackUrlFromContext();
			var request = consumer.PrepareRequestUserAuthorization(callback, extraParameters, null);
			consumer.Channel.Send(request);
		}

	    /// <summary>
		/// Gets the Gmail address book's contents.
		/// </summary>
		/// <param name="consumer">The Google consumer.</param>
		/// <param name="accessToken">The access token previously retrieved.</param>
		/// <param name="maxResults">The maximum number of entries to return. If you want to receive all of the contacts, rather than only the default maximum, you can specify a very large number here.</param>
		/// <param name="startIndex">The 1-based index of the first result to be retrieved (for paging).</param>
		/// <returns>An XML document returned by Google.</returns>
		public static XDocument GetContacts(ConsumerBase consumer, string accessToken, int maxResults/* = 25*/, int startIndex/* = 1*/) {
			if (consumer == null) {
				throw new ArgumentNullException("consumer");
			}

			var extraData = new Dictionary<string, string>() {
				{ "start-index", startIndex.ToString(CultureInfo.InvariantCulture) },
				{ "max-results", maxResults.ToString(CultureInfo.InvariantCulture) },
			};
			var request = consumer.PrepareAuthorizedRequest(GetContactsEndpoint, accessToken, extraData);

			// Enable gzip compression.  Google only compresses the response for recognized user agent headers. - Mike Lim
			request.AutomaticDecompression = DecompressionMethods.GZip;
			request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16";

			var response = consumer.Channel.WebRequestHandler.GetResponse(request);
			string body = response.GetResponseReader().ReadToEnd();
			XDocument result = XDocument.Parse(body);
			return result;
		}

		public static XDocument GetGmailInfo(ConsumerBase consumer, string accessToken)
		{
			if (consumer == null)
			{
				throw new ArgumentNullException("consumer");
			}

			var request = consumer.PrepareAuthorizedRequest(GetGmailEndpoint, accessToken, new Dictionary<string, string>());

			// Enable gzip compression.  Google only compresses the response for recognized user agent headers. - Mike Lim
			request.AutomaticDecompression = DecompressionMethods.GZip;
			request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16";

			var response = consumer.Channel.WebRequestHandler.GetResponse(request);
			string body = response.GetResponseReader().ReadToEnd();
			XDocument result = XDocument.Parse(body);
			return result;
		}

        //public static Stream GetProfileImage(ConsumerBase consumer, string accessToken, string email)
        //{
        //    if (consumer == null)
        //    {
        //        throw new ArgumentNullException("consumer");
        //    }

        //    var request = consumer.PrepareAuthorizedRequest(GetProfileImageEndpoint(email), accessToken, new Dictionary<string, string>());

        //    // Enable gzip compression.  Google only compresses the response for recognized user agent headers. - Mike Lim
        //    request.AutomaticDecompression = DecompressionMethods.GZip;
        //    request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16";

        //    var response = consumer.Channel.WebRequestHandler.GetResponse(request);
        //    return response.ResponseStream;
        //}

		/// <summary>
		/// Gets the scope URI in Google's format.
		/// </summary>
		/// <param name="scope">The scope, which may include one or several Google applications.</param>
		/// <returns>A space-delimited list of URIs for the requested Google applications.</returns>
		public static string GetScopeUri(Applications scope) {
			return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
		}
	}
}
